@dinoxx/dinox-cli 1.0.20 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +72 -139
  2. package/dist/auth/userInfo.d.ts +8 -1
  3. package/dist/auth/userInfo.js +38 -6
  4. package/dist/cli.js +1 -2
  5. package/dist/cliTypes.d.ts +1 -0
  6. package/dist/commands/auth/index.js +11 -4
  7. package/dist/commands/boxes/index.js +274 -54
  8. package/dist/commands/boxes/model.d.ts +34 -0
  9. package/dist/commands/boxes/model.js +78 -0
  10. package/dist/commands/boxes/operations.d.ts +86 -0
  11. package/dist/commands/boxes/operations.js +415 -0
  12. package/dist/commands/boxes/repo.d.ts +13 -6
  13. package/dist/commands/boxes/repo.js +272 -149
  14. package/dist/commands/boxes/store.d.ts +22 -0
  15. package/dist/commands/boxes/store.js +143 -0
  16. package/dist/commands/graph/index.js +11 -38
  17. package/dist/commands/mcp/noteContentTools.d.ts +222 -0
  18. package/dist/commands/mcp/noteContentTools.js +390 -0
  19. package/dist/commands/mcp/server.d.ts +3 -0
  20. package/dist/commands/mcp/server.js +43 -15
  21. package/dist/commands/mcp/tools.js +3 -1
  22. package/dist/commands/mcp/types.d.ts +6 -2
  23. package/dist/commands/meta/index.js +8 -24
  24. package/dist/commands/meta/repo.d.ts +0 -3
  25. package/dist/commands/meta/repo.js +1 -4
  26. package/dist/commands/notes/actions.d.ts +2 -0
  27. package/dist/commands/notes/actions.js +149 -55
  28. package/dist/commands/notes/bulk.d.ts +34 -0
  29. package/dist/commands/notes/bulk.js +204 -0
  30. package/dist/commands/notes/contentEngine.d.ts +95 -0
  31. package/dist/commands/notes/contentEngine.js +652 -0
  32. package/dist/commands/notes/contentPatch.d.ts +84 -0
  33. package/dist/commands/notes/contentPatch.js +337 -0
  34. package/dist/commands/notes/contentPatchCommand.d.ts +3 -0
  35. package/dist/commands/notes/contentPatchCommand.js +126 -0
  36. package/dist/commands/notes/descriptors.js +194 -42
  37. package/dist/commands/notes/export.d.ts +21 -0
  38. package/dist/commands/notes/export.js +224 -0
  39. package/dist/commands/notes/metadataMutation.d.ts +28 -0
  40. package/dist/commands/notes/metadataMutation.js +127 -0
  41. package/dist/commands/notes/repo.d.ts +20 -0
  42. package/dist/commands/notes/repo.js +135 -18
  43. package/dist/commands/notes/runtime.d.ts +3 -12
  44. package/dist/commands/notes/runtime.js +4 -30
  45. package/dist/commands/notes/searchSql.js +1 -1
  46. package/dist/commands/prompt/index.js +17 -47
  47. package/dist/commands/prompt/repo.js +2 -2
  48. package/dist/commands/runtime.d.ts +34 -0
  49. package/dist/commands/runtime.js +110 -0
  50. package/dist/commands/schema/globalOptions.d.ts +1 -0
  51. package/dist/commands/schema/globalOptions.js +1 -0
  52. package/dist/commands/schema/renderSkills.js +21 -7
  53. package/dist/commands/schema/spec.d.ts +1 -2
  54. package/dist/commands/schema/spec.js +2 -3
  55. package/dist/commands/schema/types.d.ts +0 -13
  56. package/dist/commands/storage/index.js +32 -62
  57. package/dist/commands/sync.js +22 -5
  58. package/dist/commands/tags/content.d.ts +32 -0
  59. package/dist/commands/tags/content.js +243 -0
  60. package/dist/commands/tags/index.js +287 -51
  61. package/dist/commands/tags/model.d.ts +38 -0
  62. package/dist/commands/tags/model.js +100 -0
  63. package/dist/commands/tags/operations.d.ts +105 -0
  64. package/dist/commands/tags/operations.js +438 -0
  65. package/dist/commands/tags/repo.d.ts +5 -1
  66. package/dist/commands/tags/repo.js +142 -218
  67. package/dist/commands/tags/store.d.ts +28 -0
  68. package/dist/commands/tags/store.js +166 -0
  69. package/dist/commands/todo/index.js +32 -63
  70. package/dist/commands/writeDryRun.d.ts +25 -0
  71. package/dist/commands/writeDryRun.js +31 -1
  72. package/dist/config/resolve.js +2 -2
  73. package/dist/daemon/index.d.ts +2 -0
  74. package/dist/daemon/index.js +176 -58
  75. package/dist/powersync/runtime.d.ts +1 -0
  76. package/dist/powersync/runtime.js +16 -0
  77. package/dist/powersync/schema/content.d.ts +2 -0
  78. package/dist/powersync/schema/content.js +2 -0
  79. package/dist/powersync/schema/index.d.ts +2 -0
  80. package/dist/powersync/syncGate.d.ts +24 -0
  81. package/dist/powersync/syncGate.js +36 -0
  82. package/dist/skills/check.d.ts +1 -1
  83. package/dist/skills/check.js +27 -2
  84. package/dist/utils/authToken.d.ts +2 -0
  85. package/dist/utils/authToken.js +7 -2
  86. package/dist/utils/noteContentEngine.d.ts +333 -0
  87. package/dist/utils/noteContentEngine.js +740 -0
  88. package/dist/utils/tiptapMarkdownSchema.js +31 -0
  89. package/package.json +4 -8
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Dinox CLI 用户手册
2
2
 
3
- `dino` 是 Dinox 的命令行工具,用来在本地管理和同步你的 Dinox 数据,包括:笔记、待办、标签、卡片盒、Prompt、知识图谱,以及给 AI 客户端使用的 MCP 服务。
3
+ `dino` 是 Dinox 的命令行工具,用来在本地管理和同步你的 Dinox 数据,包括:笔记、待办、标签、卡片盒、Prompt、知识图谱,以及适合脚本和 AI 助手调用的结构化命令输出。
4
4
 
5
5
  这份 README 按“先上手、再进阶、最后查表”的顺序重写,尽量做到:
6
6
 
@@ -20,7 +20,7 @@
20
20
  - [4. 使用前必须知道的 6 个规则](#4-使用前必须知道的-6-个规则)
21
21
  - [5. 日常操作教程](#5-日常操作教程)
22
22
  - [6. 完整命令速查](#6-完整命令速查)
23
- - [7. AI / Cursor / MCP 集成](#7-ai--cursor--mcp-集成)
23
+ - [7. AI / 脚本自动化](#7-ai--脚本自动化)
24
24
  - [8. 常见问题](#8-常见问题)
25
25
  - [9. 配置文件、数据库与环境变量](#9-配置文件数据库与环境变量)
26
26
  - [10. 如何查看帮助](#10-如何查看帮助)
@@ -36,14 +36,13 @@ Dinox CLI 适合这几类场景:
36
36
  3. 从笔记里的任务列表中搜索待办,或追加/创建待办
37
37
  4. 维护标签、卡片盒、Prompt 等结构化信息
38
38
  5. 查询笔记之间的链接关系和知识图谱
39
- 6. 给 Cursor / AI Agent 暴露 MCP 工具接口
40
- 7. 用 `--format json` 输出结构化结果,方便脚本和 AI 调用
39
+ 6. 用 `--format json` 输出结构化结果,方便脚本和 AI 调用
41
40
 
42
41
  你可以把它理解为:
43
42
 
44
43
  - 普通用户:一个更高效的 Dinox 数据管理工具
45
44
  - 进阶用户:一个可脚本化的本地知识库命令行入口
46
- - AI 用户:一个可被 MCP / 自动化工具调用的本地数据服务入口
45
+ - AI 用户:一个可通过结构化命令和 schema 稳定调用的本地知识库入口
47
46
 
48
47
  ---
49
48
 
@@ -121,8 +120,9 @@ dino auth login "<你的 token>"
121
120
  说明:
122
121
 
123
122
  - 这里填的是登录 token 本体
124
- - 不要加 `Bearer ` 前缀
123
+ - `dino auth login` 建议传 token 本体,不要加 `Bearer ` 前缀
125
124
  - 登录时 CLI 会顺便验证连接是否可用
125
+ - 自动化或 CI 可以改用 `DINOX_TOKEN` 环境变量注入 token;它优先于本地配置,并支持 token 本体或 `Bearer ...`
126
126
 
127
127
  ### 第 2 步:同步数据到本地
128
128
 
@@ -409,52 +409,57 @@ dino note create \
409
409
  --dry-run
410
410
  ```
411
411
 
412
- ### 5.4 更新笔记标签、卡片盒、收藏状态
412
+ ### 5.4 整理笔记元数据与正文
413
413
 
414
- #### 更新单条笔记的标签
414
+ #### `note update`:显式 ID 的全量替换
415
415
 
416
416
  ```bash
417
- dino note update <note-id> --tags "工作,AI"
417
+ dino note update <note-id> --tags "工作,AI" --dry-run
418
+ dino note update <note-id> --boxes "Inbox" --starred true --dry-run
418
419
  ```
419
420
 
420
- #### 更新单条笔记的卡片盒
421
+ `note update` 面向明确的笔记 ID,`--tags` 和 `--boxes` 都是完整替换,不是追加。清空标签或卡片盒时传 `[]`:
421
422
 
422
423
  ```bash
423
- dino note update <note-id> --boxes "Inbox"
424
+ dino note update <note-id> --tags '[]'
425
+ dino note update <note-id> --boxes '[]'
424
426
  ```
425
427
 
426
- #### 设置收藏状态
428
+ #### `note tag` / `note move`:显式 ID 的增量整理
427
429
 
428
430
  ```bash
429
- dino note update <note-id> --starred true
430
- dino note update <note-id> --starred false
431
+ dino note tag <note-id> --add "工作,AI" --dry-run
432
+ dino note tag --ids @./note-ids.txt --remove "Inbox" --dry-run
433
+ dino note move <note-id> --add "Project/Inbox" --dry-run
434
+ dino note move --ids @./note-ids.txt --replace "Project" --dry-run
431
435
  ```
432
436
 
433
- #### 使用专门的收藏命令
437
+ `note tag` 管标签,`note move` 管卡片盒归属;它们的 `--add` / `--remove` 会保留现有值,`--replace` 才会覆盖整个列表。`note move` 不移动文件,也不改正文。
438
+
439
+ #### `note bulk`:按过滤器批量整理
434
440
 
435
441
  ```bash
436
- dino note star <note-id>
437
- dino note unstar <note-id>
442
+ dino note bulk --tags "Inbox" --tag-add "AI" --format json --dry-run
443
+ dino note bulk --days 30 --boxes "Work/Inbox" --box-replace "Archive/Review" --expected-count 12 --confirm --format json
438
444
  ```
439
445
 
440
- #### 批量更新多条笔记
446
+ `note bulk` 只接受受控过滤器,不支持 `--sql` 作为写入目标筛选。真实写入必须先 dry-run,再带 `--confirm --expected-count <n>`。
447
+
448
+ #### `note patch`:结构化编辑正文
441
449
 
442
450
  ```bash
443
- dino note update --ids @./note-ids.txt --boxes "Project"
444
- dino note star --ids @./note-ids.txt
451
+ dino note content-read <note-id> --format json
452
+ dino note patch <note-id> --append-section "复盘" --content @./section.md --read-token <token> --format json
445
453
  ```
446
454
 
447
- 重要说明:
455
+ `note patch` 只用于正文结构化编辑。真实写入前必须先 `content-read`,并传回 `readToken`;替换媒体、表格、未知节点等受保护 block 时,还需要显式 `--allow-protected-replace`。
448
456
 
449
- - `note update` 是“全量替换”标签/卡片盒,不是追加
450
- - 如果你传 `--tags "工作,AI"`,最终标签就会变成这两个
451
- - 如果你想清空标签或卡片盒,可以传 `[]`
452
-
453
- 例如:
457
+ #### 收藏状态
454
458
 
455
459
  ```bash
456
- dino note update <note-id> --tags '[]'
457
- dino note update <note-id> --boxes '[]'
460
+ dino note star <note-id>
461
+ dino note unstar <note-id>
462
+ dino note star --ids @./note-ids.txt
458
463
  ```
459
464
 
460
465
  ### 5.5 删除笔记
@@ -858,27 +863,7 @@ dino schema todo.update --format json
858
863
  - 你要让 AI / 脚本可靠调用 CLI
859
864
  - 你想快速查看命令风险级别、输出结构、示例
860
865
 
861
- ### 6.14 `mcp`:启动 MCP 服务
862
-
863
- ```bash
864
- dino mcp serve
865
- dino mcp serve --host 127.0.0.1 --port 45137
866
- ```
867
-
868
- 如果绑定到非本机地址(例如 `0.0.0.0`),必须提供 token:
869
-
870
- ```bash
871
- dino mcp serve --host 0.0.0.0 --port 45137 --token "<your-token>"
872
- ```
873
-
874
- 或使用环境变量:
875
-
876
- ```bash
877
- export DINOX_MCP_TOKEN="<your-token>"
878
- dino mcp serve --host 0.0.0.0 --port 45137
879
- ```
880
-
881
- ### 6.15 `daemon`:管理后台进程
866
+ ### 6.14 `daemon`:管理后台进程
882
867
 
883
868
  ```bash
884
869
  dino daemon start
@@ -893,113 +878,59 @@ dino daemon stop
893
878
  dino daemon start --no-detach
894
879
  ```
895
880
 
896
- ### 6.16 `info`:查看版本与技能仓库地址
897
-
898
- ```bash
899
- dino info
900
- ```
901
-
902
- ### 6.17 `update`:升级 CLI
903
-
904
- ```bash
905
- dino update
906
- ```
907
-
908
- ---
909
-
910
- ## 7. AI / Cursor / MCP 集成
911
-
912
- 如果你希望让 Cursor、MCP Client 或其他 AI 工具直接调用 Dinox 数据,这一节最重要。
913
-
914
- ### 7.1 启动 MCP 服务
915
-
916
- 默认只监听本机回环地址:
881
+ `daemon` 是公开 CLI 模块,也可以通过 schema 查询:
917
882
 
918
883
  ```bash
919
- dino mcp serve
884
+ dino schema daemon.start --format json
920
885
  ```
921
886
 
922
- 默认地址通常是:
923
-
924
- - Host:`127.0.0.1`
925
- - Port:`45137`
926
-
927
- 手动指定地址和端口:
887
+ ### 6.15 `info`:查看版本与技能仓库地址
928
888
 
929
889
  ```bash
930
- dino mcp serve --host 127.0.0.1 --port 45137
890
+ dino info
931
891
  ```
932
892
 
933
- ### 7.2 非本机访问时必须加 token
934
-
935
- 如果你不是监听 `127.0.0.1` / `localhost`,而是希望局域网其他机器访问,那么必须加 token。
893
+ ### 6.16 `update`:升级 CLI
936
894
 
937
895
  ```bash
938
- dino mcp serve --host 0.0.0.0 --port 45137 --token "<your-token>"
896
+ dino update
939
897
  ```
940
898
 
941
- 服务端支持以下鉴权头:
899
+ ---
942
900
 
943
- 1. `Authorization: Bearer <token>`
944
- 2. `x-dinox-token: <token>`
901
+ ## 7. AI / 脚本自动化
945
902
 
946
- ### 7.3 用 curl 快速测试连通性
903
+ 如果你希望让 AI 助手或脚本可靠调用 Dinox 数据,推荐直接使用 CLI 命令的结构化输出。
947
904
 
948
- #### 初始化握手
905
+ ### 7.1 使用 JSON 输出
949
906
 
950
907
  ```bash
951
- curl -X POST http://127.0.0.1:45137 \
952
- -H 'content-type: application/json' \
953
- --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
908
+ dino note search "dinox" --limit 5 --format json
909
+ dino note detail <id> --format json
910
+ dino todo search --status uncompleted --format json
954
911
  ```
955
912
 
956
- #### 列出工具
913
+ `--format json` 会返回稳定的结构化 envelope,适合被脚本、自动化任务和 AI Agent 解析。
957
914
 
958
- ```bash
959
- curl -X POST http://127.0.0.1:45137 \
960
- -H 'content-type: application/json' \
961
- --data '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
962
- ```
915
+ ### 7.2 先读取命令 schema
963
916
 
964
- #### 调用一个工具(示例:搜索笔记)
917
+ 不确定某个命令支持哪些参数、输出哪些字段时,先读取 schema:
965
918
 
966
919
  ```bash
967
- curl -X POST http://127.0.0.1:45137 \
968
- -H 'content-type: application/json' \
969
- --data '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"dinox_search_notes","arguments":{"query":"dinox","limit":5}}}'
970
- ```
971
-
972
- ### 7.4 Cursor 配置示例
973
-
974
- 你可以在以下任一位置创建 MCP 配置文件:
975
-
976
- 1. 项目级:`.cursor/mcp.json`
977
- 2. 全局:`~/.cursor/mcp.json`
978
-
979
- 示例配置:
980
-
981
- ```json
982
- {
983
- "mcpServers": {
984
- "dinox": {
985
- "url": "http://127.0.0.1:45137",
986
- "headers": {
987
- "Authorization": "Bearer ${env:DINOX_MCP_TOKEN}"
988
- }
989
- }
990
- }
991
- }
920
+ dino schema
921
+ dino schema note.search --format json
922
+ dino schema todo.update --format json
992
923
  ```
993
924
 
994
- 如果你只在本机回环地址使用,而且服务端没有启用 token,那么可以不写 `headers`。
925
+ schema 包含命令路径、风险级别、参数、选项、输出字段和示例。写操作建议先使用 `--dry-run` 确认结果。
995
926
 
996
927
  ---
997
928
 
998
929
  ## 8. 常见问题
999
930
 
1000
- ### Q1:报错 `Missing persisted userId. Run dino auth login first.`
931
+ ### Q1:报错 `Missing resolved userId for the current authorization`
1001
932
 
1002
- 原因:你还没有完成登录,或登录信息没有成功保存。
933
+ 原因:你还没有完成登录,或当前 `DINOX_TOKEN` 环境变量覆盖了本地 token,CLI 需要先在线解析这个 token 对应的用户身份。
1003
934
 
1004
935
  解决:
1005
936
 
@@ -1008,6 +939,13 @@ dino auth login "<token>"
1008
939
  dino sync
1009
940
  ```
1010
941
 
942
+ 如果你使用环境变量登录,确认 `DINOX_TOKEN` 已设置后直接运行一次在线命令:
943
+
944
+ ```bash
945
+ dino auth status --format json
946
+ dino sync
947
+ ```
948
+
1011
949
  ### Q2:创建笔记时报 `Unknown tags` 或 `Unknown zettel box names`
1012
950
 
1013
951
  原因:你引用了不存在的标签或卡片盒。
@@ -1048,6 +986,12 @@ dino note update <note-id> --tags "工作,AI"
1048
986
 
1049
987
  执行后,目标笔记的标签会被替换成这两个,而不是在原来基础上追加。
1050
988
 
989
+ 追加标签请使用:
990
+
991
+ ```bash
992
+ dino note tag <note-id> --add "工作,AI"
993
+ ```
994
+
1051
995
  ### Q5:`todo append` 不传 `--note-id` 时会发生什么?
1052
996
 
1053
997
  CLI 会自动选一条最近的、适合追加任务的笔记作为目标。
@@ -1068,17 +1012,7 @@ dino todo append --note-id <note-id> --task "补交报销单"
1068
1012
  dino note create --title "测试" --content "@.\note.md" --type note
1069
1013
  ```
1070
1014
 
1071
- ### Q7:`mcp serve` 绑定 `0.0.0.0` 时为什么报 token 错误?
1072
-
1073
- 因为只要不是本机回环地址,就必须提供鉴权 token。
1074
-
1075
- 例如:
1076
-
1077
- ```bash
1078
- dino mcp serve --host 0.0.0.0 --port 45137 --token "<your-token>"
1079
- ```
1080
-
1081
- ### Q8:怎么知道某个命令到底支持哪些参数?
1015
+ ### Q7:怎么知道某个命令到底支持哪些参数?
1082
1016
 
1083
1017
  最直接的方法:
1084
1018
 
@@ -1121,9 +1055,8 @@ dino schema todo.update --format json
1121
1055
  | --- | --- |
1122
1056
  | `DINOX_CONFIG_DIR` | 覆盖配置目录 |
1123
1057
  | `DINOX_DATA_DIR` | 覆盖本地数据目录 |
1124
- | `DINOX_AUTHORIZATION` | 用环境变量注入登录 token |
1058
+ | `DINOX_TOKEN` | 用环境变量注入登录 token,优先级高于本地配置;可传 token 本体或 `Bearer ...` |
1125
1059
  | `DINOX_SYNC_TIMEOUT_MS` | 覆盖同步超时 |
1126
- | `DINOX_MCP_TOKEN` | 给 `dino mcp serve` 提供默认 token |
1127
1060
 
1128
1061
  ### 9.3 macOS / Linux 与 Windows 的多行命令差异
1129
1062
 
@@ -1186,6 +1119,6 @@ dino schema <path> --format json
1186
1119
  4. `dino note create`
1187
1120
  5. `dino todo search`
1188
1121
  6. `dino tag add` / `dino box add`
1189
- 7. `dino mcp serve`
1122
+ 7. `dino schema`
1190
1123
 
1191
1124
  这样会最快建立完整使用感。
@@ -14,4 +14,11 @@ export declare function fetchUserInfoWithAuthorization(input: {
14
14
  signal?: AbortSignal;
15
15
  }): Promise<AuthIdentity>;
16
16
  export declare function refreshAndPersistAuthIdentity(rawConfig: DinoxConfig, resolvedConfig: ResolvedDinoxConfig): Promise<AuthIdentity>;
17
- export declare function resolveAuthIdentity(rawConfig: DinoxConfig, resolvedConfig: ResolvedDinoxConfig): Promise<AuthIdentity>;
17
+ export declare function hydrateResolvedAuthIdentity(resolvedConfig: ResolvedDinoxConfig, options?: {
18
+ timeoutMs?: number;
19
+ }): Promise<{
20
+ config: ResolvedDinoxConfig;
21
+ identity: AuthIdentity | null;
22
+ hydrated: boolean;
23
+ }>;
24
+ export declare function resolveAuthIdentity(resolvedConfig: ResolvedDinoxConfig): Promise<AuthIdentity>;
@@ -136,16 +136,48 @@ export async function refreshAndPersistAuthIdentity(rawConfig, resolvedConfig) {
136
136
  await saveConfig(nextConfig);
137
137
  return identity;
138
138
  }
139
- export async function resolveAuthIdentity(rawConfig, resolvedConfig) {
140
- const savedUserId = normalizeOptionalString(rawConfig.auth?.userId ?? resolvedConfig.auth.userId);
141
- const syncedAt = normalizeOptionalString(rawConfig.auth?.userInfoSyncedAt ?? resolvedConfig.auth.userInfoSyncedAt);
139
+ function authIdentityFromResolvedConfig(resolvedConfig) {
140
+ const savedUserId = normalizeOptionalString(resolvedConfig.auth.userId);
141
+ const syncedAt = normalizeOptionalString(resolvedConfig.auth.userInfoSyncedAt);
142
142
  if (savedUserId) {
143
143
  return {
144
144
  userId: savedUserId,
145
- username: normalizeOptionalNullableString(rawConfig.auth?.username ?? resolvedConfig.auth.username),
146
- email: normalizeOptionalNullableString(rawConfig.auth?.email ?? resolvedConfig.auth.email),
145
+ username: normalizeOptionalNullableString(resolvedConfig.auth.username),
146
+ email: normalizeOptionalNullableString(resolvedConfig.auth.email),
147
147
  syncedAt: syncedAt ?? new Date().toISOString(),
148
148
  };
149
149
  }
150
- throw new DinoxError('Missing persisted userId. Run `dino auth login` first.');
150
+ return null;
151
+ }
152
+ export async function hydrateResolvedAuthIdentity(resolvedConfig, options = {}) {
153
+ const existing = authIdentityFromResolvedConfig(resolvedConfig);
154
+ if (existing || !resolvedConfig.auth.authorization) {
155
+ return { config: resolvedConfig, identity: existing, hydrated: false };
156
+ }
157
+ const identity = await fetchUserInfoWithAuthorization({
158
+ authorization: resolvedConfig.auth.authorization,
159
+ config: resolvedConfig,
160
+ timeoutMs: options.timeoutMs,
161
+ });
162
+ return {
163
+ config: {
164
+ ...resolvedConfig,
165
+ auth: {
166
+ ...resolvedConfig.auth,
167
+ userId: identity.userId,
168
+ username: identity.username,
169
+ email: identity.email,
170
+ userInfoSyncedAt: identity.syncedAt,
171
+ },
172
+ },
173
+ identity,
174
+ hydrated: true,
175
+ };
176
+ }
177
+ export async function resolveAuthIdentity(resolvedConfig) {
178
+ const identity = authIdentityFromResolvedConfig(resolvedConfig);
179
+ if (identity) {
180
+ return identity;
181
+ }
182
+ throw new DinoxError('Missing resolved userId for the current authorization. Run `dino auth login` first, or set DINOX_TOKEN and run an online command so the CLI can resolve the token identity.');
151
183
  }
package/dist/cli.js CHANGED
@@ -5,7 +5,6 @@ import { registerConfigCommands } from './commands/config/index.js';
5
5
  import { registerDaemonCommands } from './daemon/index.js';
6
6
  import { registerGraphCommands } from './commands/graph/index.js';
7
7
  import { registerInfoCommand } from './commands/info/index.js';
8
- import { registerMCPCommands } from './commands/mcp/index.js';
9
8
  import { registerMetaCommands } from './commands/meta/index.js';
10
9
  import { registerNoteCommands } from './commands/notes/index.js';
11
10
  import { registerPromptCommands } from './commands/prompt/index.js';
@@ -29,6 +28,7 @@ export function createCli() {
29
28
  .option('--json', 'legacy machine-readable YAML output (equivalent to --format yaml)')
30
29
  .option('--format <format>', 'structured output format: yaml | json')
31
30
  .option('--offline', 'skip connect/sync and only use local cache')
31
+ .option('--require-sync', 'fail if the local PowerSync cache cannot be confirmed fresh before reading')
32
32
  .option('--sync-timeout <ms>', 'override sync/connect timeout (milliseconds)')
33
33
  .option('--verbose', 'enable verbose logging');
34
34
  program.hook('preAction', (_thisCommand, actionCommand) => {
@@ -44,7 +44,6 @@ export function createCli() {
44
44
  registerDaemonCommands(program);
45
45
  registerGraphCommands(program);
46
46
  registerInfoCommand(program);
47
- registerMCPCommands(program);
48
47
  registerMetaCommands(program);
49
48
  registerSyncCommand(program);
50
49
  registerUpdateCommand(program);
@@ -1,6 +1,7 @@
1
1
  export type GlobalOptions = {
2
2
  json?: boolean;
3
3
  offline?: boolean;
4
+ requireSync?: boolean;
4
5
  syncTimeout?: string;
5
6
  verbose?: boolean;
6
7
  };
@@ -1,5 +1,5 @@
1
1
  import fs from 'node:fs/promises';
2
- import { buildPendingAuthConfig, fetchUserInfoWithAuthorization, mergeAuthIdentityIntoConfig, } from '../../auth/userInfo.js';
2
+ import { buildPendingAuthConfig, fetchUserInfoWithAuthorization, hydrateResolvedAuthIdentity, mergeAuthIdentityIntoConfig, } from '../../auth/userInfo.js';
3
3
  import { getLegacyPowerSyncDbPath, getPowerSyncDbPath } from '../../config/paths.js';
4
4
  import { loadConfig, saveConfig } from '../../config/store.js';
5
5
  import { resolveConfig } from '../../config/resolve.js';
@@ -8,7 +8,7 @@ import { connectPowerSync, getStatusSnapshot } from '../../powersync/runtime.js'
8
8
  import { runSingleSyncPass } from '../../powersync/syncPass.js';
9
9
  import { registerCommandGroup } from '../descriptor/register.js';
10
10
  import { FORMAT_OPTIONS, OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
11
- import { normalizeAuthToken } from '../../utils/authToken.js';
11
+ import { normalizeAuthToken, readEnvAuthToken } from '../../utils/authToken.js';
12
12
  import { DinoxError } from '../../utils/errors.js';
13
13
  import { getRequestedStructuredOutputFormat, printHumanReadableLine, printSuccess, } from '../../utils/output.js';
14
14
  import { redactAuthorization } from '../../utils/redact.js';
@@ -26,6 +26,10 @@ async function runAuthLogin(context) {
26
26
  if (!rawToken) {
27
27
  throw new DinoxError('Token is required');
28
28
  }
29
+ const envToken = readEnvAuthToken();
30
+ if (envToken && envToken !== rawToken) {
31
+ throw new DinoxError('DINOX_TOKEN is set and differs from the login token. Unset DINOX_TOKEN before running `dino auth login`, or use the same token.');
32
+ }
29
33
  const previousConfig = await loadConfig();
30
34
  const pendingConfig = buildPendingAuthConfig(previousConfig, rawToken);
31
35
  const resolved = resolveConfig(pendingConfig);
@@ -107,10 +111,13 @@ async function runAuthLogout(context) {
107
111
  }
108
112
  async function runAuthStatus(context) {
109
113
  const config = await loadConfig();
110
- const resolved = resolveConfig(config);
114
+ const baseResolved = resolveConfig(config);
111
115
  const offline = Boolean(context.globals.offline);
112
116
  const structuredOutput = getRequestedStructuredOutputFormat() !== null;
113
- const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? resolved.sync.timeoutMs;
117
+ const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? baseResolved.sync.timeoutMs;
118
+ const { config: resolved } = offline
119
+ ? { config: baseResolved }
120
+ : await hydrateResolvedAuthIdentity(baseResolved, { timeoutMs });
114
121
  const dbPath = getPowerSyncDbPath(resolved.auth.userId);
115
122
  const base = {
116
123
  loggedIn: Boolean(resolved.auth.authorization),